package network

import (
	"crypto/tls"
	"fmt"
	"net"
	"sync/atomic"
	"time"

	"gitee.com/doraemon1996/bunny/goroutine"
	"gitee.com/doraemon1996/bunny/structure"
	"gitee.com/doraemon1996/bunny/timer"
	"gitee.com/doraemon1996/bunny/uuid"
	"github.com/gorilla/websocket"
)

// Websocket连接
type WSConn struct {
	conn

	rawConn *websocket.Conn // 底层Websocket连接
	isTLS   bool            // 是否是加密连接
}

// 新建Websocket连接
func NewWSConn(ln Listener, rawConn *websocket.Conn, isTLS bool) *WSConn {
	ws := new(WSConn)
	ws.id = uuid.GenUint64()
	ws.ln = ln
	ws.rawConn = rawConn
	ws.isTLS = isTLS
	return ws
}

// 获取连接类型
func (conn *WSConn) Type() ConnectionType {
	if conn.isTLS {
		return Connection_WSS
	} else {
		return Connection_WS
	}
}

// 获取连接概况
func (conn *WSConn) String() string {
	if conn.Type() == Connection_WSS {
		return fmt.Sprintf("WSSConn[%X][%s<-->%s]", conn.id, conn.rawConn.LocalAddr().String(), conn.rawConn.RemoteAddr().String())
	} else {
		return fmt.Sprintf("WSConn[%X][%s<-->%s]", conn.id, conn.rawConn.LocalAddr().String(), conn.rawConn.RemoteAddr().String())
	}
}

// 获取底层连接
func (conn *WSConn) RawConn() net.Conn {
	return conn.rawConn.NetConn()
}

// 获取底层Websocket连接
func (conn *WSConn) RawWSConn() *websocket.Conn {
	return conn.rawConn
}

// 开始运行连接得消息处理逻辑
func (conn *WSConn) Start(handler EventHandler) {
	if handler == nil {
		panic(ErrorEventHandlerInvalid)
	}

	conn.ctl.Start(func() {
		conn.eventHandler = handler

		// 开启定时任务
		if conn.crontabPeriod.Milliseconds() >= 1 {
			conn.crontabID = timer.Interval(conn.crontabPeriod, func() {
				conn.eventHandler.OnCrontab(conn)
			})
			if conn.crontabID == 0 {
				err := &networkError{ErrorType_Network, ErrorCrontabLuanchFailed}
				if conn.eventHandler.OnError(conn, err) {
					conn.Stop(err)
					return
				}
			}
		}

		conn.ctl.Add(2)
		goroutine.Submit(func() {
			err := conn.recvLoop()
			conn.ctl.Done()
			if conn.ctl.Opening() {
				conn.Stop(err)
			}
		})
		goroutine.Submit(func() {
			err := conn.sendLoop()
			conn.ctl.Done()
			if conn.ctl.Opening() {
				conn.Stop(err)
			}
		})
	})
}

// 停止并关闭连接
func (conn *WSConn) Stop(err Error) {
	conn.ctl.Stop(func() {
		conn.rawConn.SetReadDeadline(time.Now())
		if conn.crontabID != 0 {
			timer.Remove(conn.crontabID)
			conn.crontabID = 0
		}

		conn.ctl.Wait()

		close(conn.sendChan)
		if conn.eventHandler != nil {
			conn.eventHandler.OnDisconnect(conn, err)
		}

		switch val := conn.rawConn.UnderlyingConn().(type) {
		case *tls.Conn:
			val.CloseWrite()
		case *net.TCPConn:
			val.SetLinger(3)
		}
		conn.rawConn.Close()
	})
}

// 发送消息
func (conn *WSConn) Send(msg any) (err error) {
	if conn.ctl.Closed() {
		return &networkError{ErrorType_Network, ErrorConnectionClosed}
	}

	var (
		p           []byte            // 消息编码后的二进制数据
		customerErr error             // Encode()函数返回的错误
		buf         *structure.Buffer // 数据缓存
		packet      *WSPacket         // Websocket消息包
	)

	p, customerErr = conn.eventHandler.Encode(conn, msg)
	if customerErr != nil {
		err = &networkError{ErrorType_Customer, customerErr}
		return
	}

	if len(p) < 1 {
		return &networkError{ErrorType_Network, ErrorMessageBufferSizeTooShort}
	}

	buf = bufferPool.Get()
	buf.Write(p)

	packet = wsPacketPool.Get()
	packet.Message = buf

	switch val := msg.(type) {
	case WSPacket:
		packet.MessageType = val.MessageType
	case *WSPacket:
		packet.MessageType = val.MessageType
	default:
		packet.MessageType = websocket.BinaryMessage
	}

	conn.sendChan <- packet

	return
}

// 从底层连接读取一次消息包
func (conn *WSConn) read() (msgType int, buf []byte, err Error) {
	defer func() {
		if val := recover(); val != nil {
			err = &networkError{ErrorType_Underlying, val}
		}
	}()

	var underlyingErr error
	msgType, buf, underlyingErr = conn.rawConn.ReadMessage()
	atomic.AddUint64(&conn.readBytes, uint64(len(buf)))

	if underlyingErr != nil {
		err = &networkError{ErrorType_Underlying, underlyingErr}
	}
	return
}

// 处理消息数据
func (conn *WSConn) handle(msgType int, buf []byte) (err Error) {
	var (
		msg         any   // 解码得到的消息
		customerErr error // Decode()函数返回的错误
	)

	defer func() {
		if val := recover(); val != nil {
			err = &networkError{ErrorType_Customer, val}
		}
	}()

	msg, _, customerErr = conn.eventHandler.Decode(conn, &WSPacket{buf, msgType})
	if msg == nil {
		return
	}
	if customerErr != nil {
		err = &networkError{ErrorType_Customer, customerErr}
		return
	}

	conn.eventHandler.OnMessage(conn, msg)
	atomic.AddUint64(&conn.readPackages, 1)

	return
}

// 收消息循环
func (conn *WSConn) recvLoop() (err Error) {
	var (
		msgType int    // 读取到的消息类型
		buf     []byte // 读取到的消息数据
	)

	for !conn.ctl.Closed() {
		msgType, buf, err = conn.read()
		if err != nil {
			// 发生错误时,只能退出循环或丢弃数据重新读取
			if !conn.ctl.Closed() && conn.eventHandler.OnError(conn, err) {
				break
			} else {
				continue
			}
		}

		err = conn.handle(msgType, buf)
		if err != nil && !conn.ctl.Closed() && conn.eventHandler.OnError(conn, err) {
			break
		}
	}
	return
}

// 将消息包写到底层连接
func (conn *WSConn) write(buf *structure.Buffer, msgType int) (err Error) {
	defer func() {
		if val := recover(); val != nil {
			err = &networkError{ErrorType_Underlying, val}
		}
	}()

	underlyingErr := conn.rawConn.WriteMessage(msgType, buf.Bytes())
	if underlyingErr == nil {
		atomic.AddUint64(&conn.writeBytes, uint64(buf.Len()))
		atomic.AddUint64(&conn.writePackages, 1)
	} else {
		err = &networkError{ErrorType_Underlying, underlyingErr}
	}

	return
}

// 发消息循环
func (conn *WSConn) sendLoop() (err Error) {
	var (
		pkt *WSPacket         // 将要发送的消息包
		buf *structure.Buffer // 消息数据
	)

	for !conn.ctl.Closed() {
		if len(conn.sendChan) > 0 {
			pkt = (<-conn.sendChan).(*WSPacket)
		}

		if pkt != nil {
			buf = pkt.Message.(*structure.Buffer)
			err = conn.write(buf, pkt.MessageType)
			bufferPool.Put(buf)
			wsPacketPool.Put(pkt)
			pkt = nil

			if err != nil && !conn.ctl.Closed() && conn.eventHandler.OnError(conn, err) {
				break
			}
		}
	}
	return
}

// 发送Close消息
func (conn *WSConn) WriteClose(code int, data string) Error {
	message := websocket.FormatCloseMessage(code, data)
	underlyingErr := conn.rawConn.WriteControl(websocket.CloseMessage, message, time.Now().Add(time.Second))
	return &networkError{ErrorType_Underlying, underlyingErr}
}
